home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / BufferIO.h < prev    next >
Encoding:
Text File  |  1996-08-28  |  14.3 KB  |  319 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        BufferIO.h
  3.  
  4.     Contains:    Container Manager Buffered Data I/O Interfaces
  5.  
  6.     Written by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1992-1994 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <2>     8/26/94    EL        #1182308 Add new macro for format/extract
  15.                                     without byte swapping.
  16.          <2>      5/9/94    MB        #1162181 Changes necessary to install MMM.
  17.          <1>      2/3/94    EL        first checked in
  18.  
  19.     To Do:
  20. */
  21.  
  22. /*---------------------------------------------------------------------------*
  23.  |                                                                           |
  24.  |                            <<<  BufferIO.h  >>>                           |
  25.  |                                                                           |
  26.  |              Container Manager Buffered Data I/O Interfaces               |
  27.  |                                                                           |
  28.  |                               Ira L. Ruben                                |
  29.  |                                  8/3/92                                   |
  30.  |                                                                           |
  31.  |                     Copyright Apple Computer, Inc. 1992-1994              |
  32.  |                           All rights reserved.                            |
  33.  |                                                                           |
  34.  *---------------------------------------------------------------------------*
  35.  
  36.  This file defines the routines to allocate and free I/O buffers used for buffered reading
  37.  and writing special data in "chunks" of 1, 2, and 4 byte quantities.  This is NOT a
  38.  generalized I/O package that handles arbitrary sized data.  It is intended as an interface
  39.  to the 1, 2, and 4 byte formatting and extraction handlers for portably and efficiently
  40.  handling internal information written to the container.  This includes, for example,
  41.  updating instructions and TOC entries.  
  42.  
  43.  The main use of these I/O routines is to buffer this internal data as value data for
  44.  some value.  Thus in the case of updating instructions, the updating information is
  45.  buffered as value data for an object's special "updating" property.
  46.  
  47.  Although the main use is to write value data for a value, the buffering routines also
  48.  support I/O directly to and from the container's I/O handlers.  The buffering algorithms
  49.  are basically the same.  The only difference is that in this case a value refNum is not
  50.  supplied.  An example of such a direct handler use would be TOC I/O.
  51.  
  52.  Multiple buffers of different sizes can be set up simultaneously for multiple values.  The
  53.  routines in this file take care of buffering and deblocking as necessary.  Obviously, if
  54.  the I/O handlers are being called directly, there can only be one such buffered use of
  55.  these handlers, unless care is taken to properly protect the file position (seek) 
  56.  pointer(s).
  57.  
  58.  The use of the routines in this package follow a specific protocol as follows ("|" here
  59.  means "or" as an alternative):
  60.  
  61.  For input:
  62.  
  63.              if (setjmp(setJmpEnv)) {
  64.                 cmReleaseIOBuffer(ioBuffer);
  65.                 ERRORx(...)
  66.                 return (failure);
  67.             }
  68.             
  69.             ioBuffer = cmUseIOBuffer(container, bufferSize, &setJmpEnv);
  70.             
  71.             cmNewBufferedInputData(ioBuffer, [valueRefNum], totalAmountToRead);
  72.             cmReadBufferedData(ioBuffer, 1 | 2 | 4);
  73.                        - - -
  74.             
  75.             cmReleaseIOBuffer(ioBuffer);
  76.  
  77.  For output:
  78.   
  79.              if (setjmp(setJmpEnv)) {
  80.                 cmReleaseIOBuffer(ioBuffer);
  81.                 ERRORx(...)
  82.                 return (failure);
  83.             }
  84.  
  85.             ioBuffer = cmUseIOBuffer(container, bufferSize, &setJmpEnv);
  86.             cmNewBufferedOutputData(ioBuffer, [valueRefNum]);
  87.             
  88.             cmWriteBufferedData(ioBuffer, 1 | 2 | 4);
  89.                        - - -
  90.             
  91.             cmFlushOutputBuffer(ioBuffer);
  92.             cmReleaseIOBuffer(ioBuffer);
  93.  
  94.  A setjmp/longjmp environment is defined for both input and output.  The I/O routines
  95.  use this technique rather than having to check for errors on each I/O call.  All I/O is
  96.  beacketed between a cmUseIOBuffer() and cmReleaseIOBuffer().  cmNewBufferedInputData() is
  97.  called for each new value to read to define that value and the total amount to read.
  98.  cmNewBufferedOutputData() just defines the value for output.  cmReadBufferedData() is 
  99.  used to read 1, 2, and 4 byte quantities while cmWriteBufferedData() is used to write
  100.  those quantities.  The write also requires a cmFlushOutputBuffer() at the end to make
  101.  sure the last buffer is written.
  102.  
  103.  There are a few other special calls defined here.  But the above protocal must be 
  104.  followed to do the I/O properly.
  105. */
  106.  
  107. #ifndef __BUFFEREDIO__
  108. #define __BUFFEREDIO__
  109.  
  110. #include <setjmp.h>
  111.  
  112. #ifndef __CMTYPES__
  113. #include "CMTypes.h"
  114. #endif
  115. #ifndef __CM_API_TYPES__
  116. #include "CMAPITyp.h"
  117. #endif
  118.  
  119. struct Container;
  120. struct TOCValueHdr;
  121.  
  122.  
  123.                                                                     CM_CFUNCTIONS
  124.  
  125. void *cmUseIOBuffer(struct Container *container, long maxBufferSize, jmp_buf *ioEnv);
  126.     /*
  127.     This is used to allocate an I/O buffer and its associated control information.  The
  128.     buffer is used to buffer data for input or output using cmReadBufferedData() and
  129.     cmWriteBufferedData() respectively.
  130.     
  131.     The I/O control block pointer allocated is returned as the function result as an
  132.     anonymous "void *" pointer.  The caller should view this as a "buffer pointer" to be
  133.     passed to all the other routines in this file.
  134.     
  135.     An error is reported for allocation errors.  If the error reporter returns, NULL is
  136.     returned as the function result.
  137.     
  138.     The maximum size of the buffer to be allocated is passed.  A set setjmp/longjmp
  139.     environment variable is also passed for read and write error reporting and recovery.  The
  140.     setjmp/longjmp is used rather than have to check for errors on each I/O call of the
  141.     buffered I/O routines.
  142.      
  143.     Note, if the longjmp is taken, it is the caller's responsibility (in the setjmp code) to
  144.     report the error message.  This is done because the buffered I/O is used in a number of
  145.     contexts and a more appopriate error message can be reported by the caller.
  146.     
  147.     The I/O buffer control block allocated is added to a chain whose header is in the
  148.     updating container associated with the specified container.  The buffer should be 
  149.     released when is't use is no longer needed by calling cmReleaseIOBuffer().
  150.     */
  151.     
  152.     
  153. void cmReleaseIOBuffer(void *ioBuffer);
  154.     /*
  155.     This is called to free a buffer, ioBuffer,  allocated by cmUseIOBuffer(). If the caller
  156.     "forgets" to call this routine, then when the container is closed, cmFreeAllIOBuffers()
  157.     will be called to make sure all the buffers are free for that container.
  158.     */
  159.     
  160.  
  161. void cmFreeAllIOBuffers(struct Container *container);
  162.     /*
  163.   This is called at container close time to make sure all buffers allocated for that
  164.   container are released (freed).
  165.     */
  166.  
  167.  
  168. void cmNewBufferedOutputData(void *ioBuffer, struct TOCValueHdr *theValueHdr);
  169.     /*
  170.     This conditions the output buffer, ioBuffer, previously allocated by cmUseIOBuffer(), for
  171.     initial writing of the value data for theValueHdr.  If theValueHdr is NULL, the container
  172.     is written directly with the container's output handler.  This must be called once after
  173.     cmUseIOBuffer() and before any other calls.
  174.     */
  175.     
  176.  
  177. void cmWriteBufferedData(void *ioBuffer, CM_LONG size, CM_ULONG data4);
  178.     /*
  179.     This is called to do buffered writes of 1, 2, or 4 byte data to a container. It is "glue"
  180.     code in the sense that it is the interface to the "format data" handler.  That handler
  181.     actually places the bytes in the buffer.  The buffer used is one returned from a previous
  182.     call to cmUseIOBuffer() and passed as the ioBuffer parameter.
  183.     
  184.     A CM_UCHAR, CM_USHORT, or CM_ULONG is passed in data4 which the handler is expected to
  185.     format into the buffer as 1, 2, or 4 byte quantities respectively.  This is done for
  186.     portability reasons since the architecture we're running this code on might map
  187.     differently into standard container entities.
  188.     
  189.     The expected output size, 1, 2, or 4, is passed, and the value to put into the buffer in
  190.     data4 (always passed as a CM_ULONG).  The buffer is written as value data for the value
  191.     "refNum" passed to cmNewBufferedOutputData() (theValueHdr passed to
  192.     cmNewBufferedOutputData()).  If there is no value data (theValudHdr is NULL), then the
  193.     container's output handler is called directly.  Thus two levels of I/O are available; a
  194.     "high level" through a value refNum, and a "low level" directly using the handler.
  195.     
  196.     If the output size is negative, use its absolutely value. This is use to denote
  197.     endian-ness netural data.
  198.     
  199.     This routine handles writing the buffer as it fills.  If an error is detected, a longjmp
  200.     on the setjmp environment passed to cmUseIOBuffer() is taken.  It is that setjmp code
  201.     which is responsible for reporting the error message.  This is done because the buffered
  202.     I/O routines can be used in a number of contexts.  The caller can report a more
  203.     appropriate error message rather than a more generic one we would have to report from
  204.     here.
  205.  
  206.     Note, for direct handler writes, no seeks are done here.  It is assumed that the output
  207.     is positioned to what is to be written.  If other code does seeks, then that code should
  208.     call cmBufferedIOftell() to reseek to the proper output position.
  209.     */
  210.  
  211.  
  212. #define PUT1(data, io)     cmWriteBufferedData(io, 1, (CM_ULONG)(data))
  213. #define PUT2(data, io)     cmWriteBufferedData(io, 2, (CM_ULONG)(data))
  214. #define PUT4(data, io)     cmWriteBufferedData(io, 4, (CM_ULONG)(data))
  215. #define PUT4Direct(data, io)     cmWriteBufferedData(io, -4, (CM_ULONG)(data))
  216.     /*
  217.     cmWriteBufferedData() is never explicitly called. Rather, these macros are provided to
  218.     expand to cmWriteBufferedData() calls.
  219.     */
  220.     
  221.     
  222. void cmFlushOutputBuffer(void *ioBuffer);
  223.     /*
  224.     This routine is called to make sure that an output buffer (associated with the ioBuffer)
  225.     is flushed, i.e., fully written.  This completes the value data for the value specified
  226.     to cmNewBufferedOutputData(), or simply writes the remaining partial buffer directly if
  227.     the output handler is to be used directly (see cmNewBufferedOutputData() for further
  228.     details).
  229.     */
  230.     
  231.  
  232. void cmNewBufferedInputData(void *ioBuffer, struct TOCValueHdr *theValueHdr,
  233.                                                         CM_ULONG dataSize);
  234.     /*
  235.     This conditions an input buffer, ioBuffer, previously allocated by cmUseIOBuffer(), for
  236.     initial reading up to dataSize bytes from the value data for theValueHdr.  If theValueHdr
  237.     is NULL, the container will be read directly with the container's input handler.  The
  238.     first call to cmReadBufferedData() after this call will cause cmReadBufferedData() to
  239.     reload its buffer with "new" data.  For each new value refNum, call this routine to read
  240.     its data.  If there is no value refNum, call it once after cmUseIOBuffer().  Either way,
  241.     call it before doing the first cmReadBufferedData() or cmBufferedIOftell().
  242.     */
  243.  
  244.     
  245. CM_ULONG cmReadBufferedData(void *ioBuffer, CM_LONG size);
  246.     /*
  247.     This is called to do buffered reads of data from the container.  It is "glue" code in the
  248.     sense that it is the interface to the "extract data" handler.  That handler actually
  249.     copies the bytes from the buffer.  The buffer used is one returned from a previous
  250.     call to cmUseIOBuffer() and passed as the ioBuffer parameter.
  251.     
  252.     The handler is expected to extract 1, 2, or 4 byte quantities from the buffer and place
  253.     them in a CM_UCHAR, CM_USHORT, or CM_ULONG respectively.  This is done for portability
  254.     reasons since the architecture we're running this code on might map differently into 
  255.     standard container entities.
  256.     
  257.     The buffer is read as value data for the passed value "refNum" passed to the most recent
  258.     call to cmNewBufferedInputData().  The expected input size, 1, 2, or 4, is passed, and
  259.     the value returned as the function result (always as a CM_ULONG).
  260.  
  261.     The expected input size, 1, 2, or 4, is passed, and the value returned as the function
  262.     result (always as a CM_ULONG).  The buffer is read as value data for value "refNum" 
  263.     passed to cmNewBufferedInputData() (theValueHdr passed to cmNewBufferedInputData()).  If
  264.     there is no value data (theValueHdr is NULL), then the container's input handler is
  265.     called directly.  Thus two levels of I/O are available; a "high level" through a value
  266.     refNum, and a "low level" directly using the handler.
  267.     
  268.     If the input size is negative, use its absolutely value. This is use to denote
  269.     endian-ness netural data.
  270.     
  271.     This routine handles reading the buffer as all its data is extracted.  If an error is
  272.     detected, a longjmp on the setjmp environment passed to cmUseIOBuffer() is taken.  It is
  273.     that setjmp code which is responsible for reporting the error message.  This is done
  274.     because the buffered I/O routines can be used in a number of contexts.  The caller can
  275.     report a more appropriate error message rather than a more generic one we would have to
  276.     report from here.
  277.     
  278.     Note, the caller MUST call cmNewBufferedInputData() prior to the first call of
  279.     cmReadBufferedData() to cause the buffer to be loaded the first time and to get the
  280.     value refNum.  cmNewBufferedInputData() should be used any time a new value refNum is to
  281.     be used to read new data. 
  282.     
  283.     Also note, for direct handler reads, no seeks are done here.  It is assumed that the
  284.     input is positioned to what is to be read.  If other code does seeks, then that code
  285.     should call cmBufferedIOftell() to reseek to the proper input position.
  286.     */
  287.     
  288.     
  289. #define GET1(io) ((CM_UCHAR)cmReadBufferedData(io,  1))
  290. #define GET2(io) ((CM_USHORT)cmReadBufferedData(io, 2))
  291. #define GET4(io) ((CM_ULONG)cmReadBufferedData(io,  4))
  292. #define GET4Direct(io) ((CM_ULONG)cmReadBufferedData(io,  -4))
  293.     /*
  294.     cmReadBufferedData() is never explicitly called.  Rather, these macros are provided to
  295.     expand to cmReadBufferedData() calls.
  296.     */
  297.     
  298.     
  299. CM_ULONG cmBufferedIOftell(void *ioBuffer);
  300.     /*
  301.     This returns the current I/O position from the time cmNewBufferedOutputData() or 
  302.     cmNewBufferedInputData() was called.  For I/O to value data, the position is a value
  303.     data offset (initially always 0).  For direct handler I/O, the position is a container
  304.     offset.  This was set at the time cmNewBufferedOutputData() or cmNewBufferedInputData()
  305.     was called.  The position is updated as buffers fill and are written or reloaded when
  306.     reading.
  307.     
  308.     This routine is needed when only when there is the possibility that other code might be
  309.     doing "seeks" to the same container behind the buffered I/O routine's back!  Code doing
  310.     such seeks must be able to reseek the container position according to the value returned
  311.     here.  For value refNums, this usually is not necessary, since everything is in terms of
  312.     the value data offset rather than a container offset.  But this routine, for completeness,
  313.     will return the appropriate offset position the I/O wants to use.
  314.     */
  315.  
  316.  
  317.                                                           CM_END_CFUNCTIONS
  318. #endif
  319.